﻿using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ImgClip
{
    /// <summary>
    /// Clipping rectangle
    /// </summary>
    public class CroppingRectangle
    {
        private CroppingRectangle()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        public SKRect Source { get; private set; }


        /// <summary>
        /// 
        /// </summary>
        public List<SKRect> Sources { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public SKRect Dest { get; private set; }

        /// <summary>
        /// 已经符合
        /// </summary>
        public bool IsEligible { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public bool IsPad { get; private set; }

        private int Width { get; set; }

        private int Height { get; set; }

        private int SourceLeft { get; set; }

        private int SourceTop { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private int SourceWidth { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private int SourceHeight { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private string ResizeType { get; set; }

        /// <summary>
        /// 放大图片
        /// </summary>
        private bool Enlarge { get; set; }

        /// <summary>
        /// Calculates the cropped image size
        /// </summary>
        /// <param name="source"></param>
        /// <param name="gravity">位置</param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="offsetX"></param>
        /// <param name="offsetY"></param>
        /// <returns></returns>
        public static CroppingRectangle CropCalc(SKBitmap source, string gravity, int width, int height, int offsetX, int offsetY)
        {
            var cropRectangle = new CroppingRectangle();

            if (width > 0 && height > 0)
            {
                var s_width = source.Width;
                int s_height = source.Height;
                int _w = width, _h = height, _x = 0, _y = 0;
                if (width > s_width || height > s_height)
                    return cropRectangle;

                if (!String.IsNullOrEmpty(gravity))
                {
                    switch (gravity)
                    {
                        case "nw":
                            break;
                        case "no":
                            _x = (s_width - width) / 2;
                            break;
                        case "ne":
                            _x = s_width - width;
                            break;
                        case "we":
                            _y = (s_height - height) / 2;
                            break;
                        case "ce":
                            _x = (s_width - width) / 2;
                            _y = (s_height - height) / 2;
                            break;
                        case "ea":
                            _x = s_width - width;
                            _y = (s_height - height) / 2;
                            break;
                        case "sw":
                            _y = s_height - height;
                            break;
                        case "so":
                            _x = (s_width - width) / 2;
                            _y = s_height - height;
                            break;
                        case "se":
                            _x = s_width - width;
                            _y = s_height - height;
                            break;
                    }
                }
                _x += offsetX;
                _y += offsetY;
                if (_x + width > s_width || _y + height > s_height)
                    return cropRectangle;
                cropRectangle.IsEligible = true;
                cropRectangle.Source = SKRect.Create(_x, _y, width, height);
                cropRectangle.Dest = SKRect.Create(0, 0, width, height);
            }
            return cropRectangle;
        }


        /// <summary>
        /// Calculate to resize the image
        /// </summary>
        /// <param name="source"></param>
        /// <param name="type"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static CroppingRectangle ResizeCalc(SKBitmap source, string type, bool enlarge, int width, int height)
        {
            var cropRectangle = new CroppingRectangle()
            {
                ResizeType = type,
                Enlarge = enlarge,
                SourceWidth = source.Width,
                SourceHeight = source.Height,
                Width = width,
                Height = height
            };
            cropRectangle.ResizeFit().ResizeFill().ResizeFix().ResizePad();
            cropRectangle.Source = SKRect.Create(cropRectangle.SourceLeft, cropRectangle.SourceTop, cropRectangle.SourceWidth, cropRectangle.SourceHeight);
            cropRectangle.Dest = SKRect.Create(0, 0, cropRectangle.Width, cropRectangle.Height);
            return cropRectangle;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="gravity"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="offsetX"></param>
        /// <param name="offsetY"></param>
        /// <returns></returns>
        public static CroppingRectangle WatermarkCalc(SKBitmap source, string gravity, int width, int height, int offsetX, int offsetY)
        {
            var cropRectangle = new CroppingRectangle() { Sources = new List<SKRect>() };
            int _w = source.Width, _h = source.Height, _x = 0, _y = 0;
            bool _default = false;

            switch (gravity)
            {
                case "nw":
                    break;
                case "no":
                    _x = (width - _w) / 2;
                    break;
                case "ne":
                    _x = width - _w;
                    break;
                case "we":
                    _y = (height - _h) / 2;
                    break;
                case "ce":
                    _x = (width - _w) / 2;
                    _y = (height - _h) / 2;
                    break;
                case "ea":
                    _x = width - _w;
                    _y = (height - _h) / 2;
                    break;
                case "sw":
                    _y = height - _h;
                    break;
                case "so":
                    _x = (width - _w) / 2;
                    _y = height - _h;
                    break;
                case "se":
                    _x = width - _w;
                    _y = height - _h;
                    break;
                default:
                    _default = true;
                    var rows = (int)Math.Ceiling((float)height / _h);
                    var cols = (int)Math.Ceiling((float)width / _w);
                    int _overX = (width - cols * _w) / 2;
                    int _overY = (height - rows * _h) / 2;
                    for (int i = 0; i < rows; i++)
                    {
                        for (int j = 0; j < cols; j++)
                        {
                            cropRectangle.Sources.Add(SKRect.Create(_overX + j * _w, _overY + i * _h, _w, _h));
                        }
                    }
                    break;
            }
            _x += offsetX;
            _y += offsetY;
            cropRectangle.IsEligible = true;
            if (!_default)
                cropRectangle.Sources.Add(SKRect.Create(_x, _y, _w, _h));
            return cropRectangle;
        }

        /// <summary>
        /// 自适应，当存在w时使用w，否则使用h
        /// </summary>
        /// <returns></returns>
        private CroppingRectangle ResizeFit()
        {
            if (!this.IsEligible && this.ResizeType.Equals("fit"))
            {
                if (this.Width > 0 || this.Height > 0)
                {
                    if (this.Width > 0)
                    {
                        if ((this.Width > this.SourceWidth && this.Enlarge) || this.Width < this.SourceWidth)
                        {
                            this.IsEligible = true;
                            this.Height = this.SourceHeight * this.Width / this.SourceWidth;
                        }
                    }
                    else
                    {
                        if ((this.Height > this.SourceHeight && this.Enlarge) || this.Height < this.SourceHeight)
                        {
                            this.IsEligible = true;
                            this.Width = this.SourceWidth * this.Height / this.SourceHeight;
                        }
                    }
                }
            }
            return this;
        }

        /// <summary>
        /// 铺满，当给定尺寸大于原图尺寸时，需要设置enlarge为允许放大，否则输出原尺寸
        /// 当给定尺寸小于原图尺寸时，以最小边切割
        /// </summary>
        /// <returns></returns>
        private CroppingRectangle ResizeFill()
        {
            if (!this.IsEligible && this.ResizeType.Equals("fill"))
            {
                if (this.Width > 0 && this.Height > 0)
                {
                    int _w = this.SourceWidth, _h = this.SourceHeight;
                    if (((this.Width > _w || this.Height > _h) && this.Enlarge) || (this.Width <= _w && this.Height <= _h))
                    {
                        this.IsEligible = true;
                        if (((float)this.SourceWidth / this.SourceHeight) > ((float)this.Width / this.Height))
                        {
                            this.SourceTop = 0;
                            this.SourceWidth = _h * this.Width / this.Height;
                            this.SourceLeft = (_w - this.SourceWidth) / 2;
                        }
                        else
                        {
                            this.SourceLeft = 0;
                            this.SourceHeight = _w * this.Height / this.Width;
                            this.SourceTop = (_h - this.SourceHeight) / 2;
                        }
                    }
                }
            }
            return this;
        }

        /// <summary>
        /// 固定宽度(当图片比例不匹配时会变形)
        /// </summary>
        /// <returns></returns>
        private CroppingRectangle ResizeFix()
        {
            if (!this.IsEligible && this.ResizeType.Equals("fix"))
            {
                if (this.Width > 0 && this.Height > 0)
                {
                    if (((this.Width > this.SourceWidth || this.Height > this.SourceHeight) && this.Enlarge)
                        || (this.Width < this.SourceWidth && this.Height < this.SourceHeight))
                        this.IsEligible = true;
                }
            }
            return this;
        }

        /// <summary>
        /// 填充。转载完整图片，超出部分填充颜色
        /// </summary>
        /// <returns></returns>
        private CroppingRectangle ResizePad()
        {
            if (!this.IsEligible && this.ResizeType.Equals("pad"))
            {
                this.IsPad = true;
                if (this.Width > 0 && this.Height > 0)
                {
                    this.IsEligible = true;
                    if (((float)this.SourceWidth / this.SourceHeight) > ((float)this.Width / this.Height))
                    {
                        this.SourceLeft = 0;
                        this.SourceHeight = this.Width * this.SourceHeight / this.SourceWidth;
                        this.SourceTop = (this.Height - this.SourceHeight) / 2;
                        this.SourceWidth = this.Width;
                    }
                    else
                    {
                        this.SourceTop = 0;
                        this.SourceWidth = this.Height * this.SourceWidth / this.SourceHeight;
                        this.SourceLeft = (this.Width - this.SourceWidth) / 2;
                        this.SourceHeight = this.Height;
                    }
                }
            }
            return this;
        }
    }
}
